package com.yff.tuan.interceptor;

import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.yff.tuan.exception.AppException;
import com.yff.tuan.api.Result;
import com.yff.tuan.util.ErrorMessage;

/**
 * Global Exception Handler.
 */
@ControllerAdvice(basePackages = {"com.yff.tuan.controller"})
public class GlobalDefaultExceptionHandler{

	/** The message source. */
	@Autowired
	private MessageSource messageSource;
	
	/** The Constant LOGGER. */
	private static final Logger LOGGER = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class);

	/**
	 * Handle method argument not valid exception.
	 *
	 * @param ex the ex
	 * @return the error message
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class) 
	@ResponseStatus(HttpStatus.BAD_REQUEST)	
	@ResponseBody 
	ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {	
		
		LOGGER.error("handleMethodArgumentNotValidException", ex);
		
		FieldError fieldError = ex.getBindingResult().getFieldError();				
		
		String error = resolveLocalizedErrorMessage(fieldError);				
		StringBuffer message = new StringBuffer();				
		
		message.append(fieldError.getField()).append(" / ").append(error);
		
		return new ErrorMessage(Result.INVALID_PARAMETERS.getCode(), message.toString());
	}
			
	
	/**
	 * Handle http request method not supported exception.
	 *
	 * @param ex the ex
	 * @return the error message
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)	
	@ResponseBody 
	ErrorMessage handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
		
		LOGGER.error("handleHttpRequestMethodNotSupportedException", ex);

		return new ErrorMessage(Result.HTTP_METHOD_NOT_SUPPORTED.getCode(), ex.getLocalizedMessage());		
	}
	

	
	/**
	 * Default error handler.
	 *
	 * @param ex the ex
	 * @return the response entity
	 */
	@ExceptionHandler(value = {Exception.class, RuntimeException.class})
	ResponseEntity<ErrorMessage> defaultErrorHandler(Exception ex) {
		LOGGER.error("defaultErrorHandler {}", ex);

		Result errorCode= Result.INTERNAL_SERVER_ERROR;
		HttpStatus httpStatus = errorCode.getStatus();
		ErrorMessage errorMessage = getErrorMessage(errorCode, new Object[] {});	
		return new ResponseEntity<ErrorMessage>(errorMessage, httpStatus);
	}
	
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)	
	@ResponseBody 
	ErrorMessage handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
		
		LOGGER.error("MissingServletRequestParameterException", ex);
		return new ErrorMessage(Result.MISSING_REQUEST_PARAMETER.getCode(), ex.getLocalizedMessage());		
	}
	
	/**
	 * Handle payment service exception.
	 *
	 * @param ex the ex
	 * @return the response entity
	 */
	@ExceptionHandler(AppException.class)
	ResponseEntity<ErrorMessage> handlePaymentServiceException(AppException ex) {
		
		LOGGER.error("handleApiException", ex);

		Result errorCode= ex.getResult();
		HttpStatus httpStatus = errorCode.getStatus();
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		
		ErrorMessage errorMessage = getErrorMessage(errorCode, ex.getExtraArgs());								
		return new ResponseEntity<ErrorMessage>(errorMessage, headers, httpStatus);
	}
	

	/**
	 * Handle http message not readable exception.
	 *
	 * @param ex the ex
	 * @return the error message
	 */
	@ExceptionHandler(HttpMessageNotReadableException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)	
	@ResponseBody
	ErrorMessage handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
		
		LOGGER.error("handleHttpMessageNotReadableException",ex.toString());
		String detailError = "";
		
		if (null != ex.getMessage()){
			if(ex.getCause() instanceof JsonMappingException||ex.getCause() instanceof JsonProcessingException) {
				if(ex.getMessage().indexOf("\n")!=-1) {
					detailError = ex.getMessage().substring(0,
							ex.getMessage().indexOf("\n"));
					if(detailError.contains("[") && detailError.contains("]")){
						detailError = detailError.substring(detailError.lastIndexOf("["),detailError.lastIndexOf("]")+1);
					}
				}else {
					detailError = ex.getMessage();
					if(detailError.contains("[") && detailError.contains("]")){
						detailError = detailError.substring(detailError.lastIndexOf("["),detailError.lastIndexOf("]")+1);
					}
				}
				
			}else{
				detailError = ex.getMessage().substring(0,ex.getMessage().indexOf(":"));
				if(detailError.contains("[") && detailError.contains("]")){
					detailError = detailError.substring(detailError.lastIndexOf("["),detailError.lastIndexOf("]")+1);
				}
			}
		}
		return getErrorMessage(Result.WRONG_JSON_FORMAT, new Object[]{detailError});	
				
	}
    

	/**
	 * Resolve localized error message.
	 *
	 * @param fieldError the field error
	 * @return the string
	 */
	private String resolveLocalizedErrorMessage(FieldError fieldError) {
		Locale currentLocale = LocaleContextHolder.getLocale();
		return messageSource.getMessage(fieldError, currentLocale);
	}	
	
    /**
     * Gets the error message.
     *
     * @param errorCode the error code
     * @param args the args
     * @return the error message
     */
    private ErrorMessage getErrorMessage(Result errorCode, Object[] args) {
    	String localizeErrorMessage = getLocalizeErrorMessage(errorCode.name(), args);
    	return new ErrorMessage(errorCode.getCode(), localizeErrorMessage);
    }
    
    /**
     * Gets the localize error message.
     *
     * @param errorCode the error code
     * @param args the args
     * @return the localize error message
     */
    private String getLocalizeErrorMessage(String errorCode, Object[] args) {
        Locale locale = LocaleContextHolder.getLocale();
        return messageSource.getMessage(errorCode, args, locale);
    }
	
}
